#include <rtthread.h>
#include <touch.h>
#include "driver_touch.h"

static struct rt_i2c_client *gt91x_client;

/* hardware section */
static rt_uint8_t GT91x_CFG_TBL[] =
{
    0x80, 0x47, // config addr
    0x81, // 8047
    0xE0, 0x01, // 8048 8049
    0x10, 0x01, // 804A 804B
    GT91x_MAX_TOUCH, // 804C
    0x3D, 0x20, 0x22, // 804D-804F
    0x08, 0x28, 0x08, 0x5F, 0x41, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1A, // 8050-805F
    0x1E, 0x14, 0x89, 0x2A, 0x09, 0xCD, 0xCF, 0xB5, 0x06, 0x00, 0x00, 0x00, 0x21, 0x01, 0x1D, 0x00, // 8060-806F
    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB4, 0xEF, 0x9E, 0xD5, 0xF4, 0x07, // 8070-807F
    0x00, 0x00, 0x04, 0x88, 0xB9, 0x00, 0x83, 0xC4, 0x00, 0x7F, 0xCF, 0x00, 0x7B, 0xDB, 0x00, 0x77, // 8080-808F
    0xE8, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8090-809F
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 80A0-80AF
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x10, 0x12, 0x14, // 80B0-80BF
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 80C0-80CF
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0F, 0x10, 0x12, 0x16, 0x18, // 80D0-80DF
    0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x24, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, // 80E0-80EF
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // 80F0-80FF
};

static rt_int16_t pre_x[GT91x_MAX_TOUCH] = {-1, -1, -1, -1, -1};
static rt_int16_t pre_y[GT91x_MAX_TOUCH] = {-1, -1, -1, -1, -1};
static rt_int16_t pre_w[GT91x_MAX_TOUCH] = {-1, -1, -1, -1, -1};
static rt_uint8_t s_tp_dowm[GT91x_MAX_TOUCH];
static struct rt_touch_data *read_data;

static rt_err_t gt91x_write_reg(struct rt_i2c_client *dev, rt_uint8_t write_len, rt_uint8_t *write_data)
{
    struct rt_i2c_msg msgs;

    msgs.addr  = dev->client_addr;
    msgs.flags = RT_I2C_WR;
    msgs.buf   = write_data;
    msgs.len   = write_len;

    if (rt_i2c_transfer(dev->bus, &msgs, 1) == 1) {
        return RT_EOK;
    } else {
        return -RT_ERROR;
    }
}

static rt_err_t gt91x_read_regs(struct rt_i2c_client *dev, rt_uint8_t *cmd_buf, rt_uint8_t cmd_len, rt_uint8_t read_len, rt_uint8_t *read_buf)
{
    struct rt_i2c_msg msgs[2];

    msgs[0].addr  = dev->client_addr;
    msgs[0].flags = RT_I2C_WR;
    msgs[0].buf   = cmd_buf;
    msgs[0].len   = cmd_len;

    msgs[1].addr  = dev->client_addr;
    msgs[1].flags = RT_I2C_RD;
    msgs[1].buf   = read_buf;
    msgs[1].len   = read_len;

    if (rt_i2c_transfer(dev->bus, msgs, 2) == 2) {
        return RT_EOK;
    } else {
        return -RT_ERROR;
    }
}
static rt_err_t gt91x_get_product_id(struct rt_i2c_client *dev, rt_uint8_t *read_data)
{
    rt_uint8_t cmd_buf[2];

    cmd_buf[0] = (rt_uint8_t)(GT9XX_PROD_ID_REG >> 8);
    cmd_buf[1] = (rt_uint8_t)(GT9XX_PROD_ID_REG & 0xff);

    if (gt91x_read_regs(dev, cmd_buf, 2, 4, read_data) != RT_EOK) {
        return -RT_ERROR;
    }

    return RT_EOK;
}

static rt_err_t gt91x_get_info(struct rt_i2c_client *dev, struct rt_touch_info *info)
{
    rt_uint8_t opr_buf[7] = {0};
    rt_uint8_t cmd_buf[2];

    cmd_buf[0] = (rt_uint8_t)(GT91x_INFO_REG >> 8);
    cmd_buf[1] = (rt_uint8_t)(GT91x_INFO_REG & 0xff);

    if (gt91x_read_regs(dev, cmd_buf, 2, 6, opr_buf) != RT_EOK) {
        return -RT_ERROR;
    }

    info->range_x = (opr_buf[2] << 8) + opr_buf[1];
    info->range_y = (opr_buf[4] << 8) + opr_buf[3];
    info->point_num = opr_buf[5] & 0x0f;

    return RT_EOK;
}

static rt_err_t gt91x_get_config(struct rt_i2c_client *dev, rt_uint8_t *config)
{
    rt_uint8_t cmd_buf[2];

    cmd_buf[0] = (rt_uint8_t)(GT91x_INFO_REG >> 8);
    cmd_buf[1] = (rt_uint8_t)(GT91x_INFO_REG & 0xff);

    if (gt91x_read_regs(dev, cmd_buf, 2, sizeof(GT91x_CFG_TBL)-2, config) != RT_EOK) {
        return -RT_ERROR;
    }

    return RT_EOK;
}

static rt_err_t gt91x_get_firmware_version(struct rt_i2c_client *dev, rt_uint16_t *ver)
{
    rt_uint8_t cmd_buf[2];
    rt_uint8_t ver_buf[2];

    cmd_buf[0] = (rt_uint8_t)(GT9XX_FW_VER_REG >> 8);
    cmd_buf[1] = (rt_uint8_t)(GT9XX_FW_VER_REG & 0xff);

    if (gt91x_read_regs(dev, cmd_buf, 2, 2, ver_buf) != RT_EOK) {
        return -RT_ERROR;
    }
    *ver = (rt_uint16_t)ver_buf[0] + ((rt_uint16_t)ver_buf[1]<<8);

    return RT_EOK;
}

static rt_err_t gt91x_soft_reset(struct rt_i2c_client *dev)
{
    rt_uint8_t buf[3];

    buf[0] = (rt_uint8_t)(GT91x_RESET_REG >> 8);
    buf[1] = (rt_uint8_t)(GT91x_RESET_REG & 0xFF);
    buf[2] = 0x02;

    if (gt91x_write_reg(dev, 3, buf) != RT_EOK) {
        return -RT_ERROR;
    }

    return RT_EOK;
}

static void gt91x_touch_up(void *buf, rt_int8_t id)
{
    read_data = (struct rt_touch_data *)buf;

    if(s_tp_dowm[id] == 1) {
        s_tp_dowm[id] = 0;
        read_data[id].event = RT_TOUCH_EVENT_UP;
    } else {
        read_data[id].event = RT_TOUCH_EVENT_NONE;
    }

    read_data[id].timestamp = rt_touch_get_ts();
    read_data[id].width = pre_w[id];
    read_data[id].x_coordinate = pre_x[id];
    read_data[id].y_coordinate = pre_y[id];
    read_data[id].track_id = id;

    pre_x[id] = -1;  /* last point is none */
    pre_y[id] = -1;
    pre_w[id] = -1;
}

static void gt91x_touch_down(void *buf, rt_int8_t id, rt_int16_t x, rt_int16_t y, rt_int16_t w)
{
    read_data = (struct rt_touch_data *)buf;

    if (s_tp_dowm[id] == 1) {
        read_data[id].event = RT_TOUCH_EVENT_MOVE;

    } else {
        read_data[id].event = RT_TOUCH_EVENT_DOWN;
        s_tp_dowm[id] = 1;
    }

    read_data[id].timestamp = rt_touch_get_ts();
    read_data[id].width = w;
    read_data[id].x_coordinate = x;
    read_data[id].y_coordinate = y;
    read_data[id].track_id = id;

    pre_x[id] = x; /* save last point */
    pre_y[id] = y;
    pre_w[id] = w;
}

static rt_size_t rt_touch_ops_readpoint(struct rt_touch_device *touch, void *buf, rt_size_t touch_num)
{
	rt_uint8_t point_status = 0;
    rt_uint8_t point_num = 0;
    rt_uint8_t write_buf[3];
    rt_uint8_t cmd[2];
    rt_uint8_t read_buf[8 * GT91x_MAX_TOUCH] = {0};
    rt_uint8_t read_index;
    int8_t read_id = 0;
    int16_t input_x = 0;
    int16_t input_y = 0;
    int16_t input_w = 0;

    static rt_uint8_t pre_touch = 0;
    static int8_t pre_id[GT91x_MAX_TOUCH] = {0};

    /* point status register */
    cmd[0] = (rt_uint8_t)((GT91x_STATUS_REG >> 8) & 0xFF);
    cmd[1] = (rt_uint8_t)(GT91x_STATUS_REG & 0xFF);

    if (gt91x_read_regs(gt91x_client, cmd, 2, 1, &point_status) != RT_EOK){
        touch_num = 0;
        goto exit_;
    }

    if (point_status == 0) {            /* no data */
        touch_num = 0;
        goto exit_;
    }

    if ((point_status & 0x80) == 0) {   /* data is not ready */
        touch_num = 0;
        goto exit_;
    }

    point_num = point_status & 0x0f;  /* get point num */
    if (point_num > GT91x_MAX_TOUCH) {/* point num is not correct */
        touch_num = 0;
        goto exit_;
    }

    cmd[0] = (rt_uint8_t)((GT91x_POINT1_REG >> 8) & 0xFF);
    cmd[1] = (rt_uint8_t)(GT91x_POINT1_REG & 0xFF);

    /* read point num is read_num */
    if (gt91x_read_regs(gt91x_client, cmd, 2, 8 * touch_num, read_buf) != RT_EOK) {
        touch_num = 0;
        goto exit_;
    }

    if (pre_touch > point_num) {                                      /* point up */
        for (read_index = 0; read_index < pre_touch; read_index++) {
            rt_uint8_t j;
            for (j = 0; j < point_num; j++) {                         /* this time touch num */
                read_id = read_buf[j * 8] & 0x0F;
                if (pre_id[read_index] == read_id)                   /* this id is not free */
                    break;

                if (j >= point_num - 1) {
                    rt_uint8_t up_id;
                    up_id = pre_id[read_index];
                    gt91x_touch_up(buf, up_id);
                }
            }
        }
    }

    if (point_num) {                                                /* point down */
        rt_uint8_t off_set;
        for (read_index = 0; read_index < point_num; read_index++) {
            off_set = read_index * 8;
            read_id = read_buf[off_set] & 0x0f;
            pre_id[read_index] = read_id;
            input_x = read_buf[off_set + 1] | (read_buf[off_set + 2] << 8);	/* x */
            input_y = read_buf[off_set + 3] | (read_buf[off_set + 4] << 8);	/* y */
            input_w = read_buf[off_set + 5] | (read_buf[off_set + 6] << 8);	/* size */

            gt91x_touch_down(buf, read_id, input_x, input_y, input_w);
        }
    } else if (pre_touch) {
        for(read_index = 0; read_index < pre_touch; read_index++) {
            gt91x_touch_up(buf, pre_id[read_index]);
        }
    }

    pre_touch = point_num;

exit_:
    write_buf[0] = (rt_uint8_t)((GT91x_STATUS_REG >> 8) & 0xFF);
    write_buf[1] = (rt_uint8_t)(GT91x_STATUS_REG & 0xFF);
    write_buf[2] = 0x00;
    gt91x_write_reg(gt91x_client, 3, write_buf);
    return touch_num;
}

static rt_err_t rt_touch_ops_control(struct rt_touch_device *touch, int cmd, void *arg)
{
    rt_uint8_t buf[4];
    rt_uint8_t i = 0;

    if (cmd == RT_TOUCH_CTRL_GET_ID) {
        return -RT_EINVAL;//gt91x_get_product_id(gt91x_client, arg);
    }

    if (cmd == RT_TOUCH_CTRL_GET_INFO) {
        return -RT_EINVAL;//gt91x_get_info(gt91x_client, arg);
    }

    switch(cmd) {
    case RT_TOUCH_CTRL_SET_X_RANGE: {/* set x range */
        rt_uint16_t x_range;

        x_range = *(rt_uint16_t *)arg;

        GT91x_CFG_TBL[4] = (rt_uint8_t)(x_range >> 8);
        GT91x_CFG_TBL[3] = (rt_uint8_t)(x_range & 0xff);
    }
    break;
    case RT_TOUCH_CTRL_SET_Y_RANGE: {/* set y range */
        rt_uint16_t y_range;

        y_range = *(rt_uint16_t *)arg;

        GT91x_CFG_TBL[6] = (rt_uint8_t)(y_range >> 8);
        GT91x_CFG_TBL[5] = (rt_uint8_t)(y_range & 0xff);
    }
    break;
    case RT_TOUCH_CTRL_SET_X_TO_Y: /* change x y */
        GT91x_CFG_TBL[8] ^= (1 << 3);
    break;
    case RT_TOUCH_CTRL_SET_MODE: {/* change int trig type */
        rt_uint16_t trig_type;
        trig_type = *(rt_uint16_t *)arg;

        switch (trig_type) {
        case RT_DEVICE_FLAG_INT_RX:
            GT91x_CFG_TBL[8] &= 0xFC;
        break;
        case RT_DEVICE_FLAG_RDONLY:
            GT91x_CFG_TBL[8] &= 0xFC;
            GT91x_CFG_TBL[8] |= 0x02;
        break;
        default:
        break;
        }
    }
    break;
    default:
    break;
    }

    if (gt91x_write_reg(gt91x_client, sizeof(GT91x_CFG_TBL), GT91x_CFG_TBL) != RT_EOK) { /* send config */
        return -RT_ERROR;
    }

    buf[0] = (rt_uint8_t)((GT91x_CHECK_SUM >> 8) & 0xFF);
    buf[1] = (rt_uint8_t)(GT91x_CHECK_SUM & 0xFF);
    buf[2] = 0;

    for (i = GTP_ADDR_LENGTH; i < sizeof(GT91x_CFG_TBL); i++) {
        buf[2] += GT91x_CFG_TBL[i];
    }

    buf[2] = (~buf[2]) + 1;
    buf[3] = 0; // not save

    gt91x_write_reg(gt91x_client, 4, buf);

    return RT_EOK;
}

static const struct rt_touch_ops touch_ops = {
    .touch_readpoint = rt_touch_ops_readpoint,
    .touch_control = rt_touch_ops_control,
};
static struct rt_touch_device touch_dev = {
    .info = {
        .type = RT_TOUCH_TYPE_CAPACITANCE,
        .vendor = RT_TOUCH_VENDOR_GT,
        .point_num = 5,
        .range_x = 480,
        .range_y = 272
    },
    .config = {
        .irq_pin = {
            .pin  = GT91x_IRQ_PIN,
            .mode = PIN_MODE_INPUT
        },
        .dev_name = "i2c2",
    },
    .ops = &touch_ops,
    .irq_handle = RT_NULL,
};

int rt_hw_iic_touch(void)
{
	rt_err_t result = RT_EOK;
    rt_uint16_t ver = 0xA5A5;

    /* hardware init */
    rt_pin_mode(GT91x_RST_PIN, PIN_MODE_OUTPUT);
    rt_pin_mode(GT91x_IRQ_PIN, PIN_MODE_OUTPUT);
    rt_pin_write(GT91x_RST_PIN, PIN_LOW);
    rt_pin_write(GT91x_IRQ_PIN, PIN_LOW);
    rt_thread_mdelay(1);
#if (GT91x_I2C_ADDRESS == 0x14)   // 0x28/0x29
    rt_pin_write(GT91x_IRQ_PIN, PIN_LOW);
    rt_pin_write(GT91x_RST_PIN, PIN_LOW);
    rt_thread_mdelay(1);
    rt_pin_write(GT91x_IRQ_PIN, PIN_HIGH);
    rt_hw_us_delay(200);
    rt_pin_write(GT91x_RST_PIN, PIN_HIGH);
    rt_thread_mdelay(10);
    rt_pin_write(GT91x_IRQ_PIN, PIN_LOW);
    rt_thread_mdelay(20);
//    rt_pin_mode(GT91x_IRQ_PIN, touch_dev.config.irq_pin.mode);
#elif (GT91x_I2C_ADDRESS == 0x5D)  // 0xBA/0xBB
    rt_pin_write(GT91x_IRQ_PIN, PIN_LOW);
    rt_pin_write(GT91x_RST_PIN, PIN_LOW);
    rt_thread_mdelay(1);
    rt_pin_write(GT91x_RST_PIN, PIN_HIGH);
    rt_thread_mdelay(10);
    rt_pin_write(GT91x_IRQ_PIN, PIN_LOW);
    rt_thread_mdelay(50);
//    rt_pin_mode(GT91x_IRQ_PIN, touch_dev.config.irq_pin.mode);
#else
    return -RT_ERROR;
#endif // 0x28 // 0xBA
    rt_thread_mdelay(1);
    /* interface bus */
    gt91x_client = (struct rt_i2c_client *)rt_calloc(1, sizeof(struct rt_i2c_client));
    gt91x_client->bus = (struct rt_i2c_bus_device *)rt_device_find(TOUCH_I2C_DEVICE_NAME);
    if (gt91x_client->bus == RT_NULL) {
        return -RT_ERROR;
    }

    if (rt_device_open((rt_device_t)gt91x_client->bus, RT_DEVICE_FLAG_RDWR) != RT_EOK) {
        return -RT_ERROR;
    }

    gt91x_client->client_addr = GT91x_I2C_ADDRESS;
    gt91x_soft_reset(gt91x_client);
    gt91x_get_firmware_version(gt91x_client, &ver);
    rt_kprintf("GT91X Firmware Version:%2x\r\n", ver);

    result = rt_hw_touch_register(&touch_dev, "touch", RT_DEVICE_FLAG_INT_RX/*RT_DEVICE_FLAG_RDONLY*/, RT_NULL);
    return result;
}
